// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: wsfuyibing <682805@qq.com>
// Date: 2025-02-10

package crontabs

import (
	"context"
	"fmt"
	"gitee.com/go-libs/log"
	"integration/app/lib/conf"
	"integration/app/lib/nod"
	"integration/app/lib/nod/api"
	"integration/app/models"
	"integration/app/services"
	"math"
	"strconv"
)

const (
	syncErpGoodsPage = 1
	syncErpGoodsSize = 50
)

// SyncErpGoodsCrontab
// 同步ERP商品信息到本地数据库.
//
// @Cron(0 */1 * * * *)
// @CronRunOnStartup(true)
type SyncErpGoodsCrontab struct{}

func (o *SyncErpGoodsCrontab) OnRun(ctx context.Context) (err error) {
	log.Infofc(ctx, `[crontab][sync:erp:goods] goods sync begin`)
	defer log.Infofc(ctx, `[crontab][sync:erp:goods] goods sync end`)

	for _, agent := range conf.Config.GetAgents() {
		if err = o.read(ctx, agent, syncErpGoodsPage, syncErpGoodsSize); err != nil {
			return
		}
	}
	return
}

// 读取商品列表.
func (o *SyncErpGoodsCrontab) read(cts context.Context, agent *conf.ErpAgent, page, size int) (err error) {
	var (
		count int
		ctx   = log.NewContextForChildInfo(cts, `[crontab][sync:erp:goods] goods sync: page="%d", size="%d"`, page, size)
		res   *api.GetGoodsListResponse
		total int64
	)

	// 1. 监听结束.
	defer func() {
		// 1.1 捕获异常.
		if r := recover(); r != nil {
			log.Fatalfc(ctx, `[crontab][sync:erp:goods] goods sync panic: fatal="%v"`, r)
			if err == nil {
				err = fmt.Errorf(`%v`, r)
			}
		}

		// 1.2 退出任务.
		if err != nil {
			log.Errorfc(ctx, `[crontab][sync:erp:goods] goods sync fail: error="%v"`, err)
			return
		}

		// 1.3 分页加载.
		if count < size {
			return
		}

		// 最大页码.
		pages := int(total) / size
		if (int(total) % size) > 0 {
			pages++
		}

		// 读取下页.
		if page < pages {
			if err = o.read(cts, agent, page+1, size); err != nil {
				return
			}
		}
	}()

	// 2. 读取列表.
	if res, err = nod.Api.GetGoodsList(ctx, agent, page, size); err != nil {
		return
	}

	// 3. 遍历商品.
	count = len(res.Info.Data)
	total = res.Info.Total
	for _, item := range res.Info.Data {
		if err = o.save(ctx, item); err != nil {
			break
		}
	}
	return
}

// 保存商品.
func (o *SyncErpGoodsCrontab) save(ctx context.Context, data *api.GetGoodsListDataResponse) (err error) {
	var (
		affects int64
		bean    *models.ErpGoods
		goods   = &models.ErpGoods{}
		service = services.NewErpGoodsService()
	)

	// 1. 格式参数.
	goods.ErpId = data.Id
	goods.ErpCode = data.Code
	goods.ErpName = data.Name

	// 1.1 税率.
	if n, ne := strconv.ParseFloat(fmt.Sprintf(`%v`, data.Tax), 64); ne == nil && n > 0 {
		if n < 1 {
			goods.ErpTax = n
		} else if n > 1 && n < 100 {
			goods.ErpTax = math.Round(n) / 100
		}
	}

	// 1.2 仓库.
	if n, ne := strconv.ParseInt(fmt.Sprintf(`%v`, data.Warehouse), 10, 32); ne == nil && n > 0 {
		goods.ErpWarehouse = int(n)
	}

	// 2. 读取记录.
	//    通过ID读取ERP商品数据.
	if bean, err = service.GetByErpId(ctx, goods.ErpId); err != nil {
		return
	}

	// 3. 添加商品.
	//    首次触发时执行添加逻辑.
	if bean == nil {
		if bean, err = service.Add(ctx, goods); err == nil {
			log.Infofc(ctx, `[crontab][sync:erp:goods] goods add: id="%v", name="%v", code="%v"`, goods.Id, goods.ErpName, goods.ErpCode)
		}
		return
	}

	// 4. 更新逻辑.
	if bean.ErpCode != goods.ErpCode || bean.ErpName != goods.ErpName || bean.ErpTax != goods.ErpTax || bean.ErpWarehouse != goods.ErpWarehouse {
		if affects, err = service.Edit(ctx, goods); err == nil {
			log.Infofc(ctx, `[crontab][sync:erp:goods] goods edit: id="%v", name="%v", code="%v", affects="%d"`, goods.Id, goods.ErpName, goods.ErpCode, affects)
		}
		return
	}

	// 5. 跳过更新.
	log.Infofc(ctx, `[crontab][sync:erp:goods] goods ignored: id="%v", name="%v", code="%v"`, goods.Id, goods.ErpName, goods.ErpCode)
	return
}
